//
//  STDSRenderTarget.cpp
//  STDRenderSDK
//
//  Created by 陈学明 on 2023/3/12.
//

#include "STDSRenderTarget.hpp"
#include "STDSGLFunctions.hpp"


float STDSRenderTarget::bgR = 0.f;
float STDSRenderTarget::bgG = 0.f;
float STDSRenderTarget::bgB = 0.f;
float STDSRenderTarget::bgA = 0.f;

void STDSRenderTarget::create(int _w, int _h, bool _dep) {
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mPreFboId);
    if (_w * _h <= 0)
        return;
    if (_w == mSizeW && _h == mSizeH && _dep == mbWithDep)
        return;
    _release();
    mSizeW = _w;
    mSizeH = _h;
    mbWithDep = _dep;
    glGenFramebuffers(1, &mFboId);
//    printf("GL>>>>mFboId:%d\n", mFboId);
    STDSGLFunctions::checkGlError("glGenFramebuffers ");

    mTexture = make_shared<STDSTexture>(mSizeW, mSizeH);
    glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                           mTexture->getId(), 0);

    int status = (int)glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        printf("Could not bind post processing frame buffer. %d", status);
        glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) mPreFboId);
    }

    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) mPreFboId);
    STDSGLFunctions::checkGlError("glBindFramebuffer");

    calcMemSize();
}

void STDSRenderTarget::createFromTexture(shared_ptr<STDSTexture> _tex, bool _dep) {
    if (!_tex)
        return;
    _release();
    mTexture = _tex;
    mSizeW = _tex->getWidth();
    mSizeH = _tex->getHeight();
    mbWithDep = _dep;

    glGenFramebuffers(1, &mFboId);

    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mPreFboId);
    int status = (int)glCheckFramebufferStatus(GL_FRAMEBUFFER);//在低端机上，这行代码能防止在for循环连续创建FBO时，mFboId还没准备好，导致下一行的glBindFramebuffer会Crash的bug
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        LOG_DEBUG("createFromTexture Fail Could not bind post processing frame buffer. currentStatus %d successStatus %d", status,
                 GL_FRAMEBUFFER_COMPLETE);
    }
    glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                           mTexture->getId(), 0);

    status = (int)glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        printf("Could not bind post processing frame buffer. %d", status);
        glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) mPreFboId);
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    if (mbWithDep)
        glBindRenderbuffer(GL_RENDERBUFFER, (GLuint) mPreDepId);
    glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) mPreFboId);
    mbTexExt = true;
}

void STDSRenderTarget::bindSelf(bool _clear) {
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mPreFboId);
    if (mbWithDep)
        glGetIntegerv(GL_RENDERBUFFER_BINDING, &mPreDepId);
    glGetIntegerv(GL_VIEWPORT, mPreViewport);
    glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
    glViewport(0, 0, mSizeW, mSizeH);
    if (_clear) {
        glClearColor(bgR, bgG, bgB, bgA);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    }
    mbActivated = true;
}

void STDSRenderTarget::restore() {
    if (mbActivated) {
        glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) mPreFboId);
        if (mbWithDep)
            glBindRenderbuffer(GL_RENDERBUFFER, (GLuint) mPreDepId);
        glViewport(mPreViewport[0], mPreViewport[1], mPreViewport[2], mPreViewport[3]);
        mbActivated = false;
    }
}

void STDSRenderTarget::resize(int _w, int _h, bool _dep) {
    if (_w * _h <= 0)
        return;

    if(mSizeW == _w && mSizeH == _h && mbWithDep == _dep)
        return;
    
    if (!(bool)glIsFramebuffer(mFboId)){
        glGenFramebuffers(1, &mFboId);
    }

    mSizeW = _w;
    mSizeH = _h;
    mbWithDep = _dep;

    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mPreFboId);

    mTexture = make_shared<STDSTexture>(mSizeW, mSizeH);
    glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                           mTexture->getId(), 0);

    int status = (int)glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        printf("Could not bind post processing frame buffer. %d", status);
        glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) mPreFboId);
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    if (mbWithDep)
        glBindRenderbuffer(GL_RENDERBUFFER, (GLuint) mPreDepId);
    glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) mPreFboId);

    calcMemSize();
}

void STDSRenderTarget::resize(int _w, int _h) {
    if (_w * _h <= 0)
        return;

    if(mSizeW == _w && mSizeH == _h)
        return;

    if (!(bool)glIsFramebuffer(mFboId)){
        glGenFramebuffers(1, &mFboId);
    }

    mSizeW = _w;
    mSizeH = _h;
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mPreFboId);

    mTexture = make_shared<STDSTexture>(mSizeW, mSizeH);
    glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                           mTexture->getId(), 0);

    int status = (int)glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        printf("Could not bind post processing frame buffer. %d", status);
        glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) mPreFboId);
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) mPreFboId);

    calcMemSize();
}

void STDSRenderTarget::_release() {
    if ((bool)glIsFramebuffer(mFboId)) {
        glDeleteFramebuffers(1, &mFboId);
        STDSGLFunctions::checkGlError("glDeleteFramebuffers");
        mFboId = 0;
    }
}
